﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Threading.Tasks;
using System.IO;
using DeviceCommon;
using System.Diagnostics;
using MsgPack.Serialization;

namespace TestClient
{
    public delegate void ResponseDataReceivedHandler(ResponseMsgPackge pack);
    public delegate void DataUpdatedHandler(string info);
    public class AsyncClient
    {
        // The port number for the remote device.  
        private const int port = 11000;

        // The response from the remote device.  
        private String response = String.Empty;

        // The request info send to the server.  
        public String Request
        {
            get;
            set;
        }

        private Socket client = null;

        public event DataUpdatedHandler UpdateData = null;
        protected void OnDataUpdated(string info)
        {
            if (UpdateData != null)
            {
                UpdateData(info);
            }
        }

        public event ResponseDataReceivedHandler DataReceived = null;
        protected void OnDataReceived(ResponseMsgPackge pack)
        {
            if (DataReceived != null)
            {
                DataReceived(pack);
            }
        }

        public AsyncClient()
        {
        }

        public void Start()
        {
            // Connect to a remote device.  
            try
            {
                // Establish the remote endpoint for the socket.  
                // The name of the   
                // remote device is "host.contoso.com".  
                IPAddress ip = IPAddress.Parse("127.0.0.1");
                IPEndPoint remoteEP = new IPEndPoint(ip, 3333);
                // Create a TCP/IP socket.  
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // Connect to the remote endpoint.  
                client.Connect(remoteEP);
                OnDataUpdated("Socket connected successful." + Environment.NewLine);
                StartReceive();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public void Stop()
        {
            if (client == null)
                return;
            // Connect to a remote device.  
            try
            {
                // Release the socket.  
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public void Send(byte[] sendData)
        {
            if (client == null)
                return;
            // Send test data to the remote device.  
            //client.Send(sendData);
            client.BeginSend(sendData, 0, sendData.Length, 0, new AsyncCallback(SendCallback), client);
        }

        public void Send()
        {
            if (client == null)
                return;
            // Send test data to the remote device.  
            Send(client, Request);
        }

        private void StartReceive()
        {
            var serializer = SerializationContext.Default.GetSerializer<ResponseMsgPackge>();
            ResponseMsgPackge pack = null;
            Task.Factory.StartNew(()=> 
            {
                while (true)
                {
                    // Create the state object.  
                    byte[] buffer = new byte[4096];                 
                    // Begin receiving the data from the remote device.  
                    int bytesRead = client.Receive(buffer, 0, StateObject.BufferSize, SocketFlags.None);
                    if (bytesRead > 0)
                    {
                        // There might be more data, so store the data received so far.  
                        //state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));                    
                        //var str = Encoding.UTF8.GetString(buffer, 0, effective);           
                        using (var stream = new MemoryStream(buffer, 0, bytesRead))
                        {
                            pack = serializer.Unpack(stream) as ResponseMsgPackge;
                        }                                          
                    }
                    if (pack != null && pack.OutParams.Length > 0)
                    {
                        response = pack.OutParams;
                        Console.WriteLine("Response received : {0}", response);
                        OnDataReceived(pack);
                    }
                    Thread.Sleep(500);
                }
            });              
        }


        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the state object and the client socket   
                // from the asynchronous state object.  
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;
                // Read data from the remote device.  
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    // There might be more data, so store the data received so far.  
                    state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));
                    // Get the rest of the data.  
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    // All the data has arrived; put it in response.  
                  
                   
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private void Send(Socket client, String data)
        {
            // Convert the string data to byte data using ASCII encoding.  
            byte[] byteData = Encoding.UTF8.GetBytes(data);
            // Begin sending the data to the remote device.  
            client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client);
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.  
                Socket client = (Socket)ar.AsyncState;
                // Complete sending the data to the remote device.  
                int bytesSent = client.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to server.", bytesSent);
                // Signal that all bytes have been sent.
                OnDataUpdated("Sent bytes to server successful." + Environment.NewLine);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}
